﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Ingenious.Graph
{
    /// <summary>
    /// This is the basic RDF triple, containing the subject, predicate and object
    /// </summary>
    public class Triple
    {
        private Uri m_subject;
        private Uri m_predicate;
        private Uri m_object;
        private object m_value;
        private string m_languageTag;

        private Type m_literalType;             // CLR equivalent of any specified XSD type

        #region Object Lifetime
        public Triple(Uri subject, Uri predicate, object value)
            : this(subject, predicate, value, typeof(string))
        {
        }

        public Triple(Uri subject, Uri predicate, object value, string languageTag)
            : this(subject, predicate, value, typeof(string))
        {
            if (string.IsNullOrEmpty(languageTag))
            {
                throw new ArgumentException("languageTag");
            }

            m_languageTag = languageTag;
        }

        public Triple(Uri subject, Uri predicate, object value, Type literalType)
        {
            if (subject == null)
            {
                throw new ArgumentNullException("subject");
            }

            if (predicate == null)
            {
                throw new ArgumentNullException("predicate");
            }

            if (value == null)
            {
                // TODO: Should be regarded as rdf:nil
                throw new ArgumentNullException("value");
            }

            if (literalType == null)
            {
                throw new ArgumentNullException("literalType");
            }

            m_subject = subject;
            m_predicate = predicate;
            m_value = value;
            m_literalType = literalType;
        }

        public Triple(Uri subject, Uri predicate, Uri objectUri)
        {
            if (subject == null)
            {
                throw new ArgumentNullException("subject");
            }

            if (predicate == null)
            {
                throw new ArgumentNullException("predicate");
            }

            if (objectUri == null)
            {
                throw new ArgumentNullException("objectUri");
            }

            m_subject = subject;
            m_predicate = predicate;
            m_object = objectUri;

            m_literalType = typeof(object);
        }
        #endregion

        #region Object Overrides
        public override string ToString()
        {
            return "[Subject: " + m_subject.ToString() + "] [Predicate: " + m_predicate.ToString() + "] [" + (IsLiteral ? ("Value: " + Value + "]") : ("Object: " + m_object.ToString() + "]"));
        }
        #endregion

        #region Properties

        public Uri Subject
        {
            get { return m_subject; }
            set
            {
                if (value == null)
                {
                    throw new InvalidOperationException("Subject not allowed to be null");
                }

                m_subject = value;
            }
        }

        public Uri Predicate
        {
            get { return m_predicate; }
            set
            {
                if (value == null)
                {
                    throw new InvalidOperationException("Predicate is not allowed to be null");
                }

                m_predicate = value;
            }
        }

        public Uri Object
        {
            get { return m_object; }
            set
            {
                m_object = value;
            }
        }

        public object Value
        {
            get { return m_value; }
            set
            {
                m_value = value;
            }
        }

        public Type LiteralType
        {
            get { return m_literalType; }
            set
            {
                if (value == null)
                {
                    throw new InvalidOperationException("Literal type is not allowed to be null");
                }

                m_literalType = value;
            }
        }

        public string LiteralTypeString
        {
            get { return m_literalType.ToString(); }
            set
            {
                m_literalType = Type.GetType(value);
            }
        }

        public string LanguageTag
        {
            get { return m_languageTag; }
            set
            {
                m_languageTag = value;
            }
        }

        public bool IsLiteral
        {
            get { return m_object == null; }
        }
        #endregion
    }

    /// <summary>
    /// Overridden triple class, allows the storage of a unique serialization ID
    /// with the triple item
    /// </summary>
    /// <typeparam name="TTripleId"></typeparam>
    public class Triple<TTripleId> : Triple
    {
        private TTripleId m_storeId;

        #region Object Lifetime
        public Triple(Uri subject, Uri predicate, object value)
            : base(subject, predicate, value)
        {
        }

        public Triple(Uri subject, Uri predicate, object value, string languageTag)
            : base(subject, predicate, value, languageTag)
        {
        }

        public Triple(Uri subject, Uri predicate, object value, Type literalType)
            : base(subject, predicate, value, literalType)
        {
        }

        public Triple(Uri subjectUri, Uri predicateUri, Uri objectUri)
            : base(subjectUri, predicateUri, objectUri)
        {
        }
        #endregion

        #region Properties

        // This is the serialization ID used by the database provider
        public TTripleId ID
        {
            get { return m_storeId; }
            set { m_storeId = value; }
        }
        #endregion
    }
}
